package com.hn658.user.core.service.impl;

import java.sql.Timestamp;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.hn658.framework.common.AbstractBusinessObject;
import com.hn658.framework.dataaccess.pagination.GenericPage;
import com.hn658.framework.logging.enums.LogCategory;
import com.hn658.framework.security.cryptography.Base64;
import com.hn658.framework.security.cryptography.Digests;
import com.hn658.framework.shared.context.UserContext;
import com.hn658.framework.shared.entity.IUser;
import com.hn658.user.appinfo.service.IAppInfoService;
import com.hn658.user.common.entity.AppInfoEO;
import com.hn658.user.common.entity.FunctionEO;
import com.hn658.user.common.entity.RoleInfoEO;
import com.hn658.user.common.entity.TreeNode;
import com.hn658.user.common.entity.UserInfoEO;
import com.hn658.user.common.enums.FunctionType;
import com.hn658.user.common.enums.ModuleType;
import com.hn658.user.common.expection.RoleException;
import com.hn658.user.common.expection.UserException;
import com.hn658.user.common.log.service.ILogService;
import com.hn658.user.core.dao.db.IUserAppDBDAO;
import com.hn658.user.core.dao.db.IUserInfoDBDAO;
import com.hn658.user.core.dao.db.IUserRoleDBDAO;
import com.hn658.user.core.dao.redis.IUserInfoRedisDAO;
import com.hn658.user.core.service.IFunctionService;
import com.hn658.user.core.service.IUserInfoService;

@Service("userInfoManager")
public class UserInfoServiceImpl extends AbstractBusinessObject implements IUserInfoService{
	
	@Autowired
	private IUserInfoDBDAO userInfoDBDAO;

	@Autowired
	private IUserInfoRedisDAO userInfoRedisDAO;
	
	@Autowired
	private ILogService logService;
	
	@Autowired
	private IUserAppDBDAO userAppDBDAO;
	
	@Autowired
	private IUserRoleDBDAO userRoleDBDAO;
	
	@Autowired
	private IFunctionService functionService;
	
	@Autowired
	private IAppInfoService appInfoService;
	
	@Override
	public UserInfoEO findUserByAccount(String loginAccount) {
		// 先从Redis中查询用户信息
		UserInfoEO userInfo = userInfoRedisDAO
				.findUserByLoginAccount(loginAccount);
		// 如Redis中为找到对应用户信息，则尝试去DB中查找
		if (null == userInfo) {
			logger.debug("在redis中未找到{}相关信息，尝试在数据库中查找。", loginAccount);
			userInfo = userInfoDBDAO.selectUniqueByProp("loginAccount", loginAccount);

			// 如过在DB中找到对应信息返回，并将信息存入Redis中
			if (null != userInfo) {
				logger.debug("在数据库中查找到{}相关信息，重新讲该信息存储到Redis。", loginAccount);
				userInfoRedisDAO.saveUser(userInfo);
			}
		} else {
			logger.debug("成功的在redis中找到{}相关信息。", loginAccount);
		}

		return userInfo;
	}

	@Override
	public UserInfoEO findUserByUid(Long uid) {
		// 先从Redis中查询用户信息
		UserInfoEO userInfo = userInfoRedisDAO.findUserByUid(uid);

		// 如Redis中为找到对应用户信息，则尝试去DB中查找
		if (null == userInfo) {
			logger.debug(LogCategory.SYSTEM, "在redis中未找到{}相关信息，尝试在数据库中查找。", uid);
			userInfo = userInfoDBDAO.selectUniqueByProp("id", uid);

			// 如过在DB中找到对应信息返回，并将信息存入Redis中
			if (null != userInfo) {
				logger.debug(LogCategory.SYSTEM, "在数据库中查找到{}相关信息，重新讲该信息存储到Redis。", uid);
				userInfoRedisDAO.saveUser(userInfo);
			}
		} else {
			logger.debug(LogCategory.SYSTEM, "成功的在redis中找到{}相关信息。", uid);
		}

		return userInfo;
	}
	
	@Override
	@Transactional
	public UserInfoEO modifyUserInfo(UserInfoEO user) throws UserException{
		if(user==null){
			throw new UserException(UserException.UserEmptyWrong);
		}
		UserInfoEO dbUser = userInfoDBDAO.selectUniqueByProp("id", user.getId());
		if(StringUtils.isBlank(user.getRealName())){
			throw new UserException(UserException.RealNameEmpty);
		}
		dbUser.setRealName(user.getRealName().trim());
		if(StringUtils.isBlank(user.getNickName())){
			throw new UserException(UserException.NickNameEmpty);
		}
		if(!StringUtils.equals(user.getNickName().trim(),dbUser.getNickName())&&
				userInfoDBDAO.selectUniqueByProp("nickName", user.getNickName())!=null){
			throw new UserException(UserException.ExitedNickName);
		}
		dbUser.setNickName(user.getNickName().trim());
		if(StringUtils.isNotBlank(user.getPhoneNumber())){
			dbUser.setPhoneNumber(user.getPhoneNumber());
		}
		if(StringUtils.isNotBlank(user.getAvatarUrl())){
			dbUser.setAvatarUrl(user.getAvatarUrl());
		}
		if(StringUtils.isNotBlank(user.getSign())){
			dbUser.setSign(user.getSign());
		}
		if(StringUtils.isNotBlank(user.getQq())){
			dbUser.setQq(user.getQq());
		}
		if(StringUtils.isNotBlank(user.getWeiXin())){
			dbUser.setWeiXin(user.getWeiXin());
		}
		dbUser.setSex(user.getSex());
		dbUser.setLastUpdateTime(new Timestamp(new Date().getTime()));
		userInfoDBDAO.update(dbUser);
		// 更新redis
		userInfoRedisDAO.saveUser(dbUser);
		return dbUser;
	}

	@Override
	public GenericPage<UserInfoEO> queryUser(Map<String, Object> queryMap,
			int limit, int start, String sortBy, boolean isAsc) {
		if(StringUtils.isEmpty(sortBy)){
			sortBy = "ID";
		}
		return userInfoDBDAO.selectByMap(queryMap, limit, start, sortBy, isAsc);
	}
	
	
	@Override
	@Transactional
	public void addUser(UserInfoEO user) throws UserException {
		if(user==null){
			throw new UserException(UserException.UserEmptyWrong);
		}
		if(StringUtils.isBlank(user.getLoginAccount())){
			throw new UserException(UserException.AccountEmptyWrong);
		}
		user.setLoginAccount(StringUtils.lowerCase(StringUtils.trim(user.getLoginAccount())));
		if(userInfoDBDAO.selectUniqueByProp("loginAccount",user.getLoginAccount())!=null){
			throw new UserException(UserException.ExitedAccount);
		}
		if(StringUtils.isBlank(user.getRealName())){
			throw new UserException(UserException.RealNameEmpty);
		}
		user.setRealName(user.getRealName().trim());
		if(StringUtils.isBlank(user.getNickName())){
			throw new UserException(UserException.NickNameEmpty);
		}
		user.setNickName(user.getNickName().trim());
		if(userInfoDBDAO.selectUniqueByProp("nickName", user.getNickName())!=null){
			throw new UserException(UserException.ExitedNickName);
		}
		if(StringUtils.isBlank(user.getQq())){
			throw new UserException(UserException.UserQqEmpty);
		}
		if(StringUtils.isBlank(user.getWeiXin())){
			throw new UserException(UserException.UserWeiXinEmpty);
		}
		if(StringUtils.isBlank(user.getPhoneNumber())){
			throw new UserException(UserException.UserPhoneNumberEmpty);
		}
		if(StringUtils.isBlank(user.getPassword().trim())){
			throw new UserException(UserException.UserPasswordEmpty);
		}
		String passwordWithCrypt = Base64.encodeBase64Binrary(
				Digests.sha1(user.getPassword().trim().getBytes(), user.getLoginAccount().getBytes()));
		user.setPassword(passwordWithCrypt);
		user.setIsDeleted(false);
		user.setCreateTime(new Timestamp(new Date().getTime()));
		user.setLastUpdateTime(new Timestamp(new Date().getTime()));
		userInfoDBDAO.insert(user);
		// 增加用户修改日志
		StringBuffer sb = new StringBuffer();
		IUser currentUser = UserContext.getCurrentUser();
		sb.append("当前管理员(").append(currentUser.getLoginAccount())
				.append(")增加了帐号为(").append(user.getLoginAccount())
				.append(")的用户");
		logService.add(ModuleType.USER, sb.toString(),
				UserContext.getCurrentUser());

		logger.debug(LogCategory.USER, sb.toString());
	}
	

	@Override
	public List<UserInfoEO> userInfoList(List<Long> uids) {
		return userInfoDBDAO.selectByIds(uids);
	}
	
	@Override
	@Transactional
	public UserInfoEO modifyCurrentUserPassword(UserInfoEO currentUser, String oldPassword, String newPassword,boolean isReset) throws UserException {
		
		if(StringUtils.isBlank(newPassword)){
			throw new UserException(UserException.UserPasswordEmpty);
		}
		
		UserInfoEO dbUser = userInfoDBDAO.selectUniqueByProp("id", currentUser.getId());

        if(!isReset){
            if(StringUtils.isBlank(oldPassword)){
                throw new UserException(UserException.UserPasswordEmpty);
            }

            //管理员重置时,需要再匹对老密码
            oldPassword  = oldPassword.trim();
            String oldPasswordWithCrypt = Base64.encodeBase64Binrary(
                    Digests.sha1(oldPassword.getBytes(), dbUser.getLoginAccount().getBytes()));
            if(!StringUtils.equals(oldPasswordWithCrypt, dbUser.getPassword())){
                throw new UserException(UserException.OldPasswordWrong);
            }
        }

		
		String passwordWithCrypt = Base64.encodeBase64Binrary(
				Digests.sha1(newPassword.getBytes(), dbUser.getLoginAccount().getBytes()));
		dbUser.setPassword(passwordWithCrypt);
		// 更新DB
		userInfoDBDAO.update(dbUser);
		// 更新redis
		userInfoRedisDAO.saveUser(dbUser);
		// 增加用户密码修改的日志
		StringBuffer sb = new StringBuffer();
		sb.append("当前用户(").append(currentUser.getLoginAccount())
				.append(")修改了密码。");
		logService.add(ModuleType.USER, sb.toString(),
				UserContext.getCurrentUser());

		logger.debug(LogCategory.USER, sb.toString());
		return dbUser;
	}
	
	@Override
	@Transactional
	public UserInfoEO enableUser(Long id) throws UserException {
		if(id==null){
			throw new UserException(UserException.UserIdEmptyWrong);
		}
		UserInfoEO dbUser = userInfoDBDAO.selectUniqueByProp("id", id);
		if(dbUser==null){
			throw new UserException(UserException.UserNotExitedWrong);
		}
		if(!dbUser.getIsDeleted()){
			throw new UserException(UserException.UserIsEnableWrong);
		}
		dbUser.setIsDeleted(false);
		userInfoDBDAO.update(dbUser);
		// 更新redis
		userInfoRedisDAO.saveUser(dbUser);
		// 增加用户启用日志
		StringBuffer sb = new StringBuffer();
		IUser currentUser = UserContext.getCurrentUser();
		sb.append("当前管理员(").append(currentUser.getLoginAccount())
				.append(")启用了帐号为(").append(dbUser.getLoginAccount())
				.append(")的用户。");
		logService.add(ModuleType.USER, sb.toString(),
				UserContext.getCurrentUser());

		logger.debug(LogCategory.USER, sb.toString());
		return dbUser;
	}

	@Override
	@Transactional
	public UserInfoEO disableUser(Long id) throws UserException {
		if(id==null){
			throw new UserException(UserException.UserIdEmptyWrong);
		}
		UserInfoEO dbUser = userInfoDBDAO.selectUniqueByProp("id", id);
		if(dbUser==null){
			throw new UserException(UserException.UserNotExitedWrong);
		}
		if(dbUser.getIsDeleted()){
			throw new UserException(UserException.UserIsDisableWrong);
		}
		dbUser.setIsDeleted(true);
		userInfoDBDAO.update(dbUser);
		// 更新redis
		userInfoRedisDAO.saveUser(dbUser);
		// 增加用户禁用日志
		StringBuffer sb = new StringBuffer();
		IUser currentUser = UserContext.getCurrentUser();
		sb.append("当前管理员(").append(currentUser.getLoginAccount())
				.append(")禁用了帐号为(").append(dbUser.getLoginAccount())
				.append(")的用户。");
		logService.add(ModuleType.USER, sb.toString(),
				UserContext.getCurrentUser());

		logger.debug(LogCategory.USER, sb.toString());
		return dbUser;
	}

	@Override
	public List<UserInfoEO> queryAllUser(Map<String, Object> queryMap) {
		return userInfoDBDAO.selectByMap(queryMap);
	}

	@Override
	public List<AppInfoEO> queryAuthedApps(Long uid) {
		return userAppDBDAO.selectAuthedApps(uid);
	}

	@Override
	public List<AppInfoEO> queryUnAuthedApps(Long uid) {
		return userAppDBDAO.selectUnAuthedApps(uid);
	}

	@Override
	@Transactional
	public void authUserApps(Long uid, List<Long> appIds) {
		if(uid==null){
			throw new UserException(UserException.UserIdEmptyWrong);
		}
		if(appIds==null){
			throw new UserException(UserException.UserAppIdsEmptyWrong);
		}
		userAppDBDAO.deleteByUid(uid);
		for(Long appId : appIds){
			userAppDBDAO.insert(uid, appId);		
		}
		userInfoRedisDAO.destroyUserCache(uid);
	}

	@Override
	@Transactional
	public void authUserRoles(Long uid, List<Long> roleIds) {
		if(uid==null){
			throw new UserException(UserException.UserIdEmptyWrong);
		}
		if(roleIds==null){
			throw new UserException(UserException.UserRoleIdsEmptyWrong);
		}
		userRoleDBDAO.deleteByUid(uid);
		for(Long roleId : roleIds){
			userRoleDBDAO.insertUserRole(uid, roleId);			
		}
		userInfoRedisDAO.destroyUserCache(uid);
		
	}

	@Override
	public List<RoleInfoEO> queryAuthedRoles(Long uid) {
		if(uid==null){
			throw new RoleException(RoleException.ROLE_ID_NULL);
		}
		List<RoleInfoEO> selectAuthedRoles = userRoleDBDAO.selectAuthedRoles(uid);
	
		return selectAuthedRoles;
	}

	@Override
	public List<RoleInfoEO> queryUnAuthedRoles(Long uid) {
		return userRoleDBDAO.selectUnAuthedRoles(uid);
	}

	@Override
	public Set<String> findUserAccessUris(Long uid) {
		// 先从Redis中查询用户访问的URl列表
		Set<String> accessUris = userInfoRedisDAO.findUserAccessUris(uid);

		// 如Redis中为找到对应用户信息，则尝试去DB中查找
		if (null == accessUris) {
			logger.debug(LogCategory.SYSTEM, "在redis中未找到{}相关信息，尝试在数据库中查找。", uid);
			loadUserCacheInfo(uid);
			accessUris = userInfoRedisDAO.findUserAccessUris(uid);
		} else {
			logger.debug(LogCategory.SYSTEM, "成功的在redis中找到{}相关信息。", uid);
		}
		return accessUris;
	}
	

	@Override
	public List<TreeNode> queryUserTreeNodes(Long uid) {
		List<TreeNode> userTreeNodes = userInfoRedisDAO.findUserAccessTreeNodes(uid);

		if (null == userTreeNodes) {
			logger.debug(LogCategory.SYSTEM, "在redis中未找到{}相关信息，尝试在数据库中查找。", uid);
			loadUserCacheInfo(uid);
			userTreeNodes = userInfoRedisDAO.findUserAccessTreeNodes(uid);
		} else {
			logger.debug(LogCategory.SYSTEM, "成功的在redis中找到{}相关信息。", uid);
		}
		return userTreeNodes;
	}

	
	@Override
	public void loadUserCacheInfo(Long uid) throws UserException {
		if (uid == null) {
			throw new UserException(UserException.UserIdEmptyWrong);
		}
		List<FunctionEO> functions = functionService.queryUserFunction(uid);
		//用户权限URL
		Set<String> functionUris = new HashSet<String>();
		//用户权限ID
		Set<Long> functionIds = new HashSet<Long>();
		//用户菜单树
		LinkedHashSet<TreeNode> treeNodes = new LinkedHashSet<TreeNode>();
		
		Map<Long, TreeNode> treeNodeMap = new HashMap<Long, TreeNode>();
		FunctionEO root = functionService.queryRootFunction();
		TreeNode<FunctionEO, TreeNode> rootNode = new TreeNode<FunctionEO, TreeNode>();
		rootNode.setId(root.getId());
		rootNode.setText(root.getFunctionName());
		rootNode.setExpanded(true);
		treeNodeMap.put(root.getId(), rootNode);
		
		for(FunctionEO fun : functions){
			functionUris.add(fun.getUri());
			functionIds.add(fun.getId());
			if(fun.getFunctionType().intValue()==FunctionType.BUTTON.getType().intValue()){
				continue;
			}
			TreeNode<FunctionEO, TreeNode> treeNode = treeNodeMap.get(fun.getId());
			if(treeNode==null){
				treeNode = new TreeNode<FunctionEO, TreeNode>();
				treeNode.setId(fun.getId());
				treeNode.setText(fun.getFunctionName());
				treeNode.setIndex(fun.getDisplayOrder());
				treeNode.setParentId(fun.getParentId());
				treeNode.setExpanded(true);
				if (FunctionType.MENU.getType()==(fun.getFunctionType())) {
					treeNode.setLeaf(true);
					treeNode.setUri(fun.getUri());
				}else{
					treeNode.setLeaf(false);
				}
			}
			LinkedHashSet<TreeNode> childrenNode = null;
			TreeNode parentNode = treeNodeMap.get(fun.getParentId());
			if(parentNode==null){
				for(FunctionEO parentFun : functions){
					if(parentFun.getId().longValue()==fun.getParentId().longValue()){
						parentNode = new TreeNode<FunctionEO, TreeNode>();
						parentNode.setId(parentFun.getId());
						parentNode.setText(parentFun.getFunctionName());
						parentNode.setIndex(parentFun.getDisplayOrder());
						parentNode.setParentId(parentFun.getParentId());
						childrenNode = new LinkedHashSet<TreeNode>();
						parentNode.setChildren(childrenNode);
						break;
					}
				}
			}
			if(parentNode!=null){
				childrenNode = parentNode.getChildren();
				if(childrenNode==null){
					childrenNode = new LinkedHashSet<TreeNode>();
					parentNode.setChildren(childrenNode);
				}
				childrenNode.add(treeNode);
				treeNodeMap.put(fun.getId(), treeNode);
				parentNode.setLeaf(false);
				parentNode.setExpanded(true);
				treeNodeMap.put(fun.getParentId(), parentNode);					
			}
		}
		treeNodes = rootNode.getChildren();
		
		userInfoRedisDAO.saveUserAccessUris(uid, functionUris);
		userInfoRedisDAO.saveUserAccessIds(uid, functionIds);
		userInfoRedisDAO.saveUserAccessTreeNodes(uid, treeNodes);
	}

	@Override
	public Set<String> findUserAccessUris(String uid) {
		// 先从Redis中查询用户访问的URl列表
		Set<String> accessUris = userInfoRedisDAO.findUserAccessUris(Long.parseLong(uid));

		// 如Redis中为找到对应用户信息，则尝试去DB中查找
		if (null == accessUris) {
			logger.debug(LogCategory.SYSTEM, "在redis中未找到{}相关信息，尝试在数据库中查找。", uid);
			loadUserCacheInfo(Long.parseLong(uid));
			accessUris = userInfoRedisDAO.findUserAccessUris(Long.parseLong(uid));
		} else {
			logger.debug(LogCategory.SYSTEM, "成功的在redis中找到{}相关信息。", uid);
		}
		return accessUris;
	}

	
	private void loadUserAccessAppInfo(Long uid){
		Set<String> accessAppKeys = new HashSet<String>();
		Set<String> accessAppUris = new HashSet<String>();
		List<AppInfoEO> accessApps = this.queryAuthedApps(uid);
		if(accessApps!=null&&accessApps.size()>0){
			for(AppInfoEO app : accessApps){
				accessAppKeys.add(app.getAppId());
				accessAppUris.add(app.getUrl());
			}
		}
		userInfoRedisDAO.saveUserAccessAppKeys(uid, accessAppKeys);
		userInfoRedisDAO.saveUserAccessAppUris(uid, accessAppUris);
	}
	
	@Override
	public Set<String> findUserAccessAppKeyByUid(String userId) {
		Long uid = Long.parseLong(userId);
		Set<String> accessAppKeys = userInfoRedisDAO.findUserAccessAppKeyByUid(uid);
		// 如Redis中为找到对应用户信息，则尝试去DB中查找
		if (null == accessAppKeys) {
			logger.debug(LogCategory.SYSTEM, "在redis中未找到{}相关信息，尝试在数据库中查找。", accessAppKeys);
			loadUserAccessAppInfo(uid);
			accessAppKeys = userInfoRedisDAO.findUserAccessAppKeyByUid(uid);
		} else {
			logger.debug(LogCategory.SYSTEM, "成功的在redis中找到{}相关信息。", uid);
		}
		return accessAppKeys;
	}
	
	@Override
	public Set<String> findUserAccessAppUriByUid(String userId) {
		Long uid = Long.parseLong(userId);
		Set<String> accessAppUris = userInfoRedisDAO.findUserAccessAppUriByUid(uid);
		// 如Redis中为找到对应用户信息，则尝试去DB中查找
		if (null == accessAppUris) {
			accessAppUris = new HashSet<String>();
			logger.debug(LogCategory.SYSTEM, "在redis中未找到{}相关信息，尝试在数据库中查找。", accessAppUris);
			loadUserAccessAppInfo(uid);
			accessAppUris = userInfoRedisDAO.findUserAccessAppUriByUid(uid);
		} else {
			logger.debug(LogCategory.SYSTEM, "成功的在redis中找到{}相关信息。", uid);
		}
		return accessAppUris;
	}

	@Override
	public GenericPage<UserInfoEO> queryAppUser(Map<String, Object> queryMap,
			int limit, int start, String sortBy, boolean isAsc) {
		if (StringUtils.isEmpty(sortBy)) {
			sortBy = "ID";
		}
		return userInfoDBDAO.selectAppUser(queryMap, limit, start, sortBy, isAsc);
	}
}
